﻿using Gateway.Common.Models;
using Gateway.Domain.Entities;
using Gateway.IApplication.BaseInput;
using Gateway.IApplication.Question;
using Gateway.IApplication.Question.Input;
using Gateway.IRepositories;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;

namespace Gateway.Application;
/// <summary>
/// 
/// </summary>
public class QuestionApp : IQuestionApp
{
    private readonly IAppRepository<Question> _repository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly IAppRepository<QuestionLanguage> _languageRepository;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="repository"></param>
    /// <param name="unitOfWork"></param>
    /// <param name="languageRepository"></param>
    public QuestionApp(IAppRepository<Question> repository, IUnitOfWork unitOfWork,
        IAppRepository<QuestionLanguage> languageRepository)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
        _languageRepository = languageRepository;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<bool> Add(CreateQuestionInput input)
    {
        var entity = new Question(questionName: input.QuestionName,
            questionDescribe: input.QuestionDescribe,
            questionCase: input.QuestionCase != null ? JsonConvert.SerializeObject(input.QuestionCase) : ""
        );
        if (input.Languages is { Count: > 0 })
        {
            foreach (var item in input.Languages)
            {
            }
        }

        await _repository.AddAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> Update(UpdateQuestionInput input)
    {
        var entity = await _repository.Entities.Include(c => c.Languages).ThenInclude(c => c.ExtensionCodes)
            .FirstOrDefaultAsync(p => p.Id.Equals(input.QuestionId));
        if (entity == null)
            throw new MyException(SystemErrorCode.DataIsNotAvailable);
        entity.Update(questionName: input.QuestionName,
            questionDescribe: input.QuestionDescribe,
            questionCase: input.QuestionCase != null ? JsonConvert.SerializeObject(input.QuestionCase) : ""
        );
        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> Remove(string id)
    {
        var entity = await _repository.Entities.Include(c => c.Languages).ThenInclude(c => c.ExtensionCodes)
            .FirstOrDefaultAsync(p => p.Id.Equals(id));
        if (entity == null)
            throw new MyException(SystemErrorCode.DataIsNotAvailable);
        entity.SetDeleted();
        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="id"></param>
    /// <param name="disbale"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> Disable(string id, string disbale)
    {
        var entity = await _repository.Entities.Include(c => c.Languages).ThenInclude(c => c.ExtensionCodes)
            .FirstOrDefaultAsync(p => p.Id.Equals(id));
        if (entity == null)
            throw new MyException(SystemErrorCode.DataIsNotAvailable);
        entity.SetDisabled(disbale);
        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> SetLanguage(UpdateQuestionLanguageInput input)
    {
        var entity = await _repository.Entities.Include(c => c.Languages).ThenInclude(c => c.ExtensionCodes)
            .FirstOrDefaultAsync(p => p.Id.Equals(input.QuestionId));
        if (entity == null)
            throw new MyException(SystemErrorCode.DataIsNotAvailable);
        List<QuestionLanguage> languages = new List<QuestionLanguage>();
        foreach (var item in input.Languages)
        {
            var tmp = new QuestionLanguage(language: item.Language,
                questionId: input.QuestionId,
                examples: item.Examples,
                examplesFileName: item.ExamplesFileName,
                execFileName: item.ExecFileName,
                extensionCodes: item.ExtensionCodes?.Select(x => new QuestionExtensionCode()
                {
                    QuestionId = input.QuestionId,
                    Code = x.Code,
                    Name = x.Name
                }).ToList() ?? new List<QuestionExtensionCode>(),
                dependCodes: item.DependCodes
            );
            languages.Add(tmp);
        }

        entity.SetLanguages(languages);
        await _repository.UpdateAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    /// <exception cref="MyException"></exception>
    public async Task<bool> RemoveLanguage(RemoveInput input)
    {
        var entity = await _languageRepository.Entities.Include(p => p.ExtensionCodes)
            .FirstOrDefaultAsync(p => p.Id.Equals(input.Id));
        if (entity == null)
            throw new MyException(SystemErrorCode.DataIsNotAvailable);
        await _languageRepository.DeleteAsync(entity);
        await _unitOfWork.CommitAsync();
        return true;
    }
}